#include "envelope.h"

#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Envelope * createEnvelope(unsigned char content, size_t size, unsigned char * data)
{
	struct Envelope * envelope = malloc(sizeof(struct Envelope));
	envelope->content = content;
	envelope->size = size;
	envelope->data = malloc(size);
	memcpy(envelope->data, data, size);
	return envelope;
}

void destroyEnvelope(struct Envelope * envelope)
{
	if(envelope)
	{
		if(envelope->data)
		{
			free(envelope->data);
		}
		free(envelope);
	}
	else
	{
		printf("WARNING: destroyEnvelope() received a null pointer.\n");
	}
}

void serializeEnvelope(struct Envelope * envelope, struct Stream * stream)
{
	if(!envelope)
	{
		printf("WARNING: serializeEnvelope() received a null pointer for envelope.\n");
	}
	else if(!stream)
	{
		printf("WARNING: serializeEnvelope() received a null pointer for stream.\n");
	}
	else
	{
		unsigned char header[6];
		header[0] = '!';
		header[1] = envelope->content;
		
		uint32_t networkByteOrderSize = htonl(envelope->size);
		memcpy(header + 2, &networkByteOrderSize, 4);
		
		push_back(stream, 6, header);
		push_back(stream, envelope->size, envelope->data);
	}
}

struct Envelope * deserializeEnvelope(struct Stream * stream)
{
	struct Envelope * envelope = 0;
	
	if(!stream)
	{
		printf("WARNING: deserializeEnvelope() received a null pointer for stream.\n");
	}
	else
	{
		unsigned int i = 0;
		
		for(i = 0; i < stream->size; ++i)
		{
			if(stream->data[i] == '!')
			{
				break;
			}
		}
		
		if(i < stream->size)
		{
			pop_front(stream, i);
			
			if(6 < stream->size)
			{
				uint32_t networkByteOrderSize;
				memcpy(&networkByteOrderSize, stream->data + 2, 4);
				
				u_int32_t hostByteOrderSize = ntohl(networkByteOrderSize);
				
				if(6 + hostByteOrderSize < stream->size)
				{
					// We have a valid Envelope
					envelope = malloc(sizeof(struct Envelope));
					envelope->content = stream->data[1];
					envelope->size = hostByteOrderSize;
					envelope->data = malloc(hostByteOrderSize);
					memcpy(envelope->data, stream->data + 6, hostByteOrderSize);
					
					pop_front(stream, 6 + hostByteOrderSize);
				}
			}
		}
		else
		{
			pop_front(stream, stream->size);
		}
		
	}
	
	return envelope;
}

void envelope_unit_test()
{
	unsigned char abc[3] = {'a', 'b', 'c'};
	//unsigned char def[3] = {'d', 'e', 'f'};
	
	struct Envelope * envelope = createEnvelope('T', 3, abc);
	
	if(envelope->content != 'T')
	{
		printf("Envelope Unit Test Failed 00\n");
		return;
	}
	if(envelope->size != 3)
	{
		printf("Envelope Unit Test Failed 01\n");
		return;
	}
	if(envelope->data[2] != 'c')
	{
		printf("Envelope Unit Test Failed 02\n");
		return;
	}
	
	struct Stream * stream = createStream();
	serializeEnvelope(envelope, stream);
	
	struct Envelope * envelope2 = deserializeEnvelope(stream);
	
	if(!envelope2)
	{
		printf("Envelope Unit Test Failed 03\n");
		return;
	}
	if(envelope2->content != envelope->content)
	{
		printf("Envelope Unit Test Failed 04\n");
		return;
	}
	if(envelope2->size != envelope->size)
	{
		printf("Envelope Unit Test Failed 05\n");
		return;
	}
	if(envelope2->data[1] != envelope->data[1])
	{
		printf("Envelope Unit Test Failed 06\n");
		return;
	}
	
	destroyEnvelope(envelope);
	destroyEnvelope(envelope2);
	destroyStream(stream);
	
	printf("Envelope Unit Test OK\n");
}
